package com.zhixing.map.service.impl;

import com.zhixing.common.domain.dto.LocationDTO;
import com.zhixing.common.domain.entity.DriverInfo;
import com.zhixing.common.domain.enums.DriverStatus;
import com.zhixing.common.domain.vo.DriverDistanceVO;
import com.zhixing.map.domain.dto.DriverLocationDTO;
import com.zhixing.map.service.LocationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static com.zhixing.common.constant.Constant.*;

@Slf4j
@Service
@RequiredArgsConstructor
public class LocationServiceImpl implements LocationService {

    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public void updateDriverLocation(DriverLocationDTO driverLocationDTO) {
        // 缓存到城市位置中
        redisTemplate.opsForGeo().add(ZHIXING_DRIVER_LOCATION + driverLocationDTO.getCityCode(),
                new Point(driverLocationDTO.getLongitude().doubleValue(), driverLocationDTO.getLatitude().doubleValue()),
                driverLocationDTO.getDriverInfo());
    }

    @Override
    public void deleteDriverLocation(String cityCode, Integer driverId) {
        // 从城市位置中删除
        redisTemplate.opsForGeo().remove(ZHIXING_DRIVER_LOCATION + cityCode,
                new DriverInfo(driverId, DriverStatus.IDLE), new DriverInfo(driverId, DriverStatus.BUSY));
    }

    @Override
    public List<DriverDistanceVO> getOrderNearbyDrivers(LocationDTO locationDTO) {
        // 从城市位置中查询订单附近司机
        Circle circle = new Circle(new Point(locationDTO.getLongitude().doubleValue(), locationDTO.getLatitude().doubleValue()),
                new Distance(MAX_RECEIVE_DISTANCE, Metrics.KILOMETERS));
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                .includeDistance().sortAscending().limit(MAX_RECEIVE_NUMBER);
        GeoResults<RedisGeoCommands.GeoLocation<Object>> radius = redisTemplate.opsForGeo().radius(ZHIXING_DRIVER_LOCATION + locationDTO.getCityCode(), circle, args);
        Boolean flag = null;
        List<DriverDistanceVO> driverIds = new ArrayList<>();
        for (GeoResult<RedisGeoCommands.GeoLocation<Object>> geoResult : Objects.requireNonNull(radius).getContent()) {
            double distance = geoResult.getDistance().getValue();
            if (driverIds.size() >= DEFAULT_RECEIVE_NUMBER || (flag != null && flag && distance >= DEFAULT_RECEIVE_DISTANCE)) break;
            DriverInfo driverInfo = (DriverInfo) geoResult.getContent().getName();
            if (driverInfo.getDriverStatus() == DriverStatus.BUSY) continue;
            if (flag == null) flag = distance < DEFAULT_RECEIVE_DISTANCE;
            driverIds.add(new DriverDistanceVO(driverInfo.getDriverId(), new BigDecimal(distance).setScale(3, RoundingMode.HALF_UP)));
        }
        return driverIds;
    }
}
